home *** CD-ROM | disk | FTP | other *** search
/ 100 Great Games for Palm OS 2 / PalmV2012301.ISO / action / The Same Game / same.c < prev    next >
C/C++ Source or Header  |  1998-05-26  |  23KB  |  857 lines

  1. /*
  2. Same - The Same Game for Palm Pilot
  3. Copyright (C) 1998 Franτois Pessaux (francois.pessaux@inria.fr)
  4.  
  5. This program is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU General Public License
  7. as published by the Free Software Foundation; either version 2
  8. of the License, or (at your option) any later version.
  9.  
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18. */
  19.  
  20.  
  21. #include <Common.h>
  22. #include <System/SysAll.h>
  23. #include <UI/UIAll.h>
  24.  
  25. #include "same.h"
  26.  
  27. #define SAME_APP_ID 'SaMe'
  28. #define SAME_DB_TYPE 'Data'
  29. #define SAME_STATE_DB_NAME "SameCurrent"
  30. #define SAME_HISCORES_DB_NAME "SameHiScores"
  31.  
  32. #define MIN_SURFACE 1
  33. #define BITS_WIDTH 16
  34. #define BITS_HEIGHT 14
  35. #define PATTERN_NUMBER 5
  36. #define PATTERN_SIZE 10
  37. #define Y_OFFSET (PATTERN_SIZE*2)
  38.  
  39. #define IS_MARKED(c) ((c) & 0x80)
  40. #define SET_MARKED(c) ((c) = (c) | 0x80)
  41. #define UNSET_MARKED(c) ((c) = (c) & 0x7F)
  42. #define GET_COLOR(c) ((c) & 0x7F)
  43. #define IS_EMPTY(c) ((c) == 0)
  44. #define SET_EMPTY(c) ((c) = 0)
  45.  
  46. #define HS_SIZE 5
  47.  
  48. #define TRUE 1
  49. #define FALSE 0
  50.  
  51. #define HISCORE_STD 0
  52. #define HISCORE_CHALLENGE 1
  53.  
  54. /* Les autres valeurs sont la couleur choisie (sauf 0) */
  55. #define CHALLENGE_GAME_UNDETERMINATED 0xFF
  56. #define STANDARD_GAME 0xFE
  57.  
  58.  
  59. /* White */
  60. #define PATTERN1 {0x0000,0x0000,0x0000,0x0000}
  61. /* Black */
  62. #define PATTERN2 {0xFFFF,0xFFFF,0xFFFF,0xFFFF}
  63. /* Dotted */
  64. #define PATTERN3 {0xAA55,0xAA55,0xAA55,0xAA55}
  65. /* Line black line white */
  66. #define PATTERN4 {0x00FF, 0x00FF, 0x00FF, 0x00FF}
  67. /* Grid */
  68. #define PATTERN5 {0x55FF, 0x55FF, 0x55FF, 0x55FF}
  69. /* Slanted line */
  70. #define PATTERN6 {0x8142, 0x2418, 0x1824, 0x4281}
  71.  
  72. #define PATUNDEF {0x0000,0x0000,0x0000,0xFF00}
  73.  
  74.  
  75. struct score_entry {
  76.   unsigned char name[6] ;
  77.   unsigned int score ;
  78. };
  79.  
  80. struct score_entry hiscores[HS_SIZE+1] ;
  81. unsigned char marked_surface ;
  82. unsigned char remaining_cases ;
  83. unsigned char current_color ;
  84. unsigned char bits[BITS_WIDTH][BITS_HEIGHT] ;
  85. unsigned int score ;
  86. unsigned char score_text[10] ;
  87. CustomPatternType pattern_array[PATTERN_NUMBER+1] = {
  88.   PATTERN1, PATTERN2, PATTERN3, PATTERN4, PATTERN5, PATTERN6 } ;
  89. CustomPatternType undefined_challenge_pattern = PATUNDEF ;
  90.  
  91. unsigned char game_running = FALSE ;
  92. unsigned char game_mode ;
  93.  
  94.  
  95.  
  96. void init_bits ()
  97. {
  98.   char x, y ;
  99.  
  100.   for (x = 0; x < BITS_WIDTH; x++) {
  101.     for (y = 0; y < BITS_HEIGHT; y++) {
  102.       bits[x][y] = (unsigned char) (SysRandom (0) % PATTERN_NUMBER) + 1 ;
  103.     }
  104.   }
  105.   /* Nombre de cases restant a effacer */
  106.   remaining_cases = BITS_WIDTH * BITS_HEIGHT ;
  107. }
  108.  
  109.  
  110. #define PUSH(v) stack[stackindex] = v ; stackindex++
  111. #define POP(v) stackindex-- ; v = stack[stackindex]
  112. /* Version ou on gere explicitement la recursion avec une pile a nous  */
  113. /* Pour avoir une version plus claire, mais non geree a la main, il    */
  114. /* faut aller voir dans le fichier "archive"                           */
  115. /* On est oblige de faire ca pour eviter de faire peter la pile. Le    */
  116. /* schema de reursion (semble, car je ne l'ai pas prouve) garantit     */
  117. /* que la taille de la recursion est bornee par la taille de la piece. */
  118. void mark_bits (char global_x_marking, char global_y_marking)
  119. {
  120.   unsigned char switchvar ;
  121.   unsigned char stack[BITS_WIDTH * BITS_HEIGHT] ;
  122.   unsigned char stackindex=0 ;
  123.  
  124.   PUSH (0) ;
  125.  
  126.  begin:
  127.   switchvar = 5;
  128.  
  129.  finish:
  130.   switch (switchvar) {
  131.   case 5:
  132.     SET_MARKED (bits[global_x_marking][global_y_marking]) ;
  133.     /* Il faudra verifier a la fin que la surface */
  134.     /* reellement marquee est superieure a 1      */
  135.     marked_surface++ ;
  136.  
  137.     /* Maintenant, on regarde si chacun des voisins est de la couleur */
  138.     /* choisie pour l'effacement, s'il n'est pas deja marque comme a  */
  139.     /* effacer, et si il se trouve bien dans la surface de jeu. Si    */
  140.     /* c'est le cas, alors on recurse un coup.                        */
  141.     global_x_marking-- ;
  142.     if ((global_x_marking >= 0) &&
  143.        (GET_COLOR (bits[global_x_marking][global_y_marking])==current_color) &&
  144.        (!IS_MARKED (bits[global_x_marking][global_y_marking])))
  145.       {
  146.     PUSH (1) ;
  147.     goto begin ; /* mark_bits () ; */
  148.       }
  149.  
  150.   case 1:
  151.   global_x_marking += 2 ;
  152.   if ((global_x_marking < BITS_WIDTH) &&
  153.       (GET_COLOR (bits[global_x_marking][global_y_marking])==current_color) &&
  154.       (!IS_MARKED (bits[global_x_marking][global_y_marking])))
  155.     {
  156.       PUSH (2) ;
  157.       goto begin ; /* mark_bits () ; */
  158.     }
  159.  
  160.   case 2:
  161.     global_x_marking-- ;
  162.     global_y_marking-- ;
  163.     if ((global_y_marking >= 0) &&
  164.       (GET_COLOR (bits[global_x_marking][global_y_marking])==current_color) &&
  165.       (!IS_MARKED (bits[global_x_marking][global_y_marking])))
  166.       {
  167.     PUSH (3) ;
  168.     goto begin ; /* mark_bits () ; */
  169.       }
  170.  
  171.   case 3:
  172.     global_y_marking += 2 ;
  173.     if ((global_y_marking < BITS_HEIGHT) &&
  174.       (GET_COLOR (bits[global_x_marking][global_y_marking])==current_color) &&
  175.       (!IS_MARKED (bits[global_x_marking][global_y_marking])))
  176.       {
  177.     PUSH (4) ;
  178.     goto begin ; /* mark_bits () ; */
  179.       }
  180.  
  181.   case 4:
  182.     global_y_marking-- ;
  183.     POP (switchvar) ;
  184.     goto finish ;
  185.   }
  186. }
  187.  
  188.  
  189.  
  190.  
  191.  
  192.  
  193. void delete_bits (void)
  194. {
  195.   char x, y, x2 ;
  196.  
  197.   for (x = 0; x < BITS_WIDTH; x++) {
  198.     /* On traite les colonnes de gauche a droite */
  199.     char next_room = BITS_HEIGHT-1 ;
  200.  
  201.     for (y = BITS_HEIGHT-1 ; y >= 0; y--) {
  202.       /* On traite chaque ligne du bas vers le haut */
  203.       if (IS_MARKED (bits[x][y])) {
  204.     /* La case est marquee, donc on la "vide" */
  205.     SET_EMPTY (bits[x][y]) ;
  206.     remaining_cases-- ;
  207.       }
  208.       else {
  209.     /* La case est a 0 (donc "vide"), donc par construction,  */
  210.     /* les cases du dessus le sont aussi. Donc on peut passer */
  211.     /* a la colonne suivante */
  212.     if (IS_EMPTY (bits[x][y])) break ;
  213.     else {
  214.       /* La case n'est pas vide et n'est pas marquee */
  215.       unsigned char tmp = bits[x][y] ;
  216.  
  217.       SET_EMPTY (bits[x][y]) ;
  218.       bits[x][next_room] = tmp ;
  219.       next_room-- ;
  220.     }
  221.       }
  222.     }
  223.   }
  224.  
  225.   /* Maintenant, il faut scaner (de droite a gauche) les colonnes */
  226.   /* vides et les rapprocher a gauche pour faire du compactage    */
  227.   for (x = BITS_WIDTH-1; x >= 0; x--) {
  228.     /* Si la case du bas est a 0, alors c'est que la colonne est vide */
  229.     if (IS_EMPTY (bits[x][BITS_HEIGHT-1])) {
  230.       /* x = indice de la colonne vide */
  231.       /* On rapproche toutes les colonnes se trouvant */
  232.       /* a droite en scanant de gauche a droite       */
  233.       for (x2= x + 1; x2 < BITS_WIDTH ; x2++) {
  234.     if (x2 < BITS_WIDTH) {
  235.       /* La colonne x2 va dans colonne x2-1 et on */
  236.       /* efface le contenu de la colonne x2.      */
  237.       for (y = 0; y < BITS_HEIGHT; y++) {
  238.         bits[x2-1][y] = bits[x2][y] ;
  239.         SET_EMPTY (bits[x2][y]) ;
  240.       }
  241.     }
  242.       }
  243.     }
  244.   }
  245. }
  246.  
  247.  
  248.  
  249. void refresh_whole (void)
  250. {
  251.   char x, y ;
  252.   RectangleType rect ;
  253.  
  254.   for (y = 0; y < BITS_HEIGHT; y++) {
  255.     for (x = 0; x < BITS_WIDTH; x++) {
  256.       WinSetPattern (pattern_array[bits[x][y]]) ;
  257.       rect.topLeft.x = x * PATTERN_SIZE ;
  258.       rect.topLeft.y = (y * PATTERN_SIZE) + Y_OFFSET ;
  259.       rect.extent.x = PATTERN_SIZE ;
  260.       rect.extent.y = PATTERN_SIZE ;
  261.       WinFillRectangle (&rect, 0) ;
  262.     }
  263.   }
  264. }
  265.  
  266.  
  267.  
  268. /* Hypothese, aucune cs n'est marquee lors de la verification */
  269. Boolean check_game_over (void)
  270. {
  271.   char x, y ;
  272.  
  273.   for (x = 0; x < BITS_WIDTH-1; x++) {
  274.     for (y = BITS_HEIGHT-1; y > 0; y--) {
  275.       if (!IS_EMPTY (bits[x][y])) {
  276.     /* La cas n'est pas vide... */
  277.     if ((bits[x][y] == bits[x][y-1]) || (bits[x][y] == bits[x+1][y]))
  278.       return (FALSE) ;
  279.       }
  280.       else {
  281.     /* La case courante est vide, donc celles du dessus aussi */
  282.     /* donc on peut passer directement a la colonne suivante */
  283.     break ;
  284.       }
  285.     }
  286.   }
  287.   /* Si on arrive ici c'est qu'on n'a rien trouve et donc le jeu est fini */
  288.   return (TRUE) ;
  289. }
  290.  
  291.  
  292. void refresh_score (void)
  293. {
  294.   StrIToA (score_text, score) ;
  295.   WinDrawChars (score_text, StrLen (score_text), 110, 0) ;
  296. }
  297.  
  298.  
  299. /* Part du principe qu'on est en mode challenge */
  300. void refresh_challenge_indicator (void)
  301. {
  302.   RectangleType rect ;
  303.  
  304.   if (game_mode != CHALLENGE_GAME_UNDETERMINATED) {
  305.     /* Si le challenge est choisi, on indique le pattern choisi */
  306.     WinSetPattern (pattern_array[game_mode]) ;
  307.   }
  308.   else {
  309.     /* Met un petit truc indiquant que le challenge */
  310.     /* n'est pas encore choisi */
  311.     WinSetPattern (undefined_challenge_pattern) ;
  312.   }
  313.  
  314.   rect.topLeft.x = 90 ;
  315.   rect.topLeft.y = 2 ;
  316.   rect.extent.x = PATTERN_SIZE ;
  317.   rect.extent.y = PATTERN_SIZE ;
  318.   WinFillRectangle (&rect, 0) ;
  319. }
  320.  
  321.  
  322. void handle_turn (char case_x, char case_y)
  323. {
  324.   /* On verifie qu'on est bien dans l'espace de jeu et que la case */
  325.   /* designee n'est pas une case vide */
  326.   if ((case_x >= 0) && (case_x < BITS_WIDTH) &&
  327.       (case_y >= 0) && (case_y < BITS_HEIGHT) &&
  328.       (!IS_EMPTY (bits[case_x][case_y]))) {
  329.  
  330.     /* Memorise la couleur actuellement pointee */
  331.     current_color = GET_COLOR (bits[case_x][case_y]) ;
  332.  
  333.     if (game_mode == CHALLENGE_GAME_UNDETERMINATED) {
  334.       /* Fixe la couleur de challenge */
  335.       game_mode = current_color ;
  336.       refresh_challenge_indicator () ;
  337.     }
  338.     else {
  339.       /* Rien n'est marque a l'origine */
  340.       marked_surface = 0 ;
  341.       /* On fait le parcours de marquage */
  342.       mark_bits (case_x, case_y) ;
  343.       /* On met a jour le damier seulement s'il y a + d'1 composante connexe */
  344.       if (marked_surface > MIN_SURFACE) {
  345.     delete_bits () ;
  346.     if (game_mode == STANDARD_GAME)
  347.       score += ((marked_surface - 2) * (marked_surface - 2)) ;
  348.     else
  349.       {
  350.         /* On n'incremente le score que si la couleur est celle choisie */
  351.         if (game_mode == current_color)
  352.           score += ((marked_surface - 2) * (marked_surface - 2)) ;
  353.       }
  354.     refresh_whole () ;
  355.     refresh_score () ;
  356.       }
  357.       else {
  358.     /* En fait, la surface est trop petite, */
  359.     /* donc remettre la case en etat */
  360.     UNSET_MARKED (bits[case_x][case_y]) ;
  361.       }
  362.     }
  363.   }
  364. }
  365.  
  366.  
  367. int SortScore (struct score_entry *se1, struct score_entry *se2)
  368. {
  369. if (se1->score == se2->score) return (0) ;
  370. if (se1->score < se2->score) return (1) ;
  371. return (-1) ;
  372. }
  373.  
  374.  
  375.  
  376. /* Charge les hiscores. Si la base n'existe pas, ca la cree */
  377. void load_hiscore (unsigned which_one)
  378. {
  379.   DmOpenRef same_db ;
  380.   LocalID db_id ;
  381.   UInt index ;
  382.   VoidPtr record ;
  383.   VoidHand handle ;
  384.  
  385.   /* Memoriser les hiscores */
  386.   db_id = DmFindDatabase (0, SAME_HISCORES_DB_NAME ) ;
  387.   if (!db_id) {
  388.     /* La base n'existe pas. On la cree from scratch */
  389.     unsigned char i ;
  390.     /* On initialise donc le hiscores from scratch */
  391.     for (i=0;i<HS_SIZE;i++) {
  392.       StrCopy (hiscores[i].name, "-----") ;
  393.       hiscores[i].score = 0 ;
  394.     }
  395.  
  396.     DmCreateDatabase (0, SAME_HISCORES_DB_NAME, SAME_APP_ID, SAME_DB_TYPE,
  397.               FALSE) ;
  398.     same_db = DmOpenDatabase (0, DmFindDatabase (0, SAME_HISCORES_DB_NAME),
  399.                   dmModeReadWrite) ;
  400.     /* Hiscores en mode standard */
  401.     index = 0 ;
  402.     handle = DmNewRecord (same_db, &index,
  403.               HS_SIZE*sizeof(struct score_entry)) ;
  404.     record = MemHandleLock (handle) ;
  405.     DmWrite (record, 0, (VoidPtr*)hiscores,
  406.          HS_SIZE*sizeof(struct score_entry)) ;
  407.     MemHandleUnlock (handle) ;
  408.     DmReleaseRecord (same_db, index, FALSE) ;
  409.  
  410.     /* Hiscore en mode challenge */
  411.     index = 1 ;
  412.     handle = DmNewRecord (same_db, &index,
  413.               HS_SIZE*sizeof(struct score_entry)) ;
  414.     record = MemHandleLock (handle) ;
  415.     DmWrite (record, 0, (VoidPtr*)hiscores,
  416.          HS_SIZE*sizeof(struct score_entry)) ;
  417.     MemHandleUnlock (handle) ;
  418.     DmReleaseRecord (same_db, index, FALSE) ;
  419.     DmCloseDatabase (same_db) ;
  420.   }
  421.   else {
  422.     /* Index des scores a charger en fonction du type demande */
  423.     if (which_one == HISCORE_STD) index = 0 ;
  424.     else index = 1 ;
  425.     /* On ouvre la base pour charger les anciens hiscores */
  426.     same_db = DmOpenDatabase (0, db_id, dmModeReadWrite) ;
  427.  
  428.     handle = DmGetRecord (same_db, index) ;
  429.     record = MemHandleLock (handle) ;
  430.     MemMove ((VoidPtr)hiscores, (VoidPtr)record,
  431.          HS_SIZE * sizeof(struct score_entry)) ;
  432.     MemHandleUnlock (handle) ;
  433.     DmReleaseRecord (same_db, index, FALSE) ;
  434.     DmCloseDatabase (same_db) ;
  435.   }
  436. }
  437.  
  438.  
  439. void game_over (void)
  440. {
  441.   /* Perfect Game */
  442.   if (remaining_cases == 0)
  443.     {
  444.       score += 1000 ;
  445.       FrmAlert (ALERT_PERFECT_ID) ;
  446.     }
  447.   game_running = FALSE ;
  448.   FrmAlert (ALERT_GOVER_ID) ;
  449.   if (game_mode == STANDARD_GAME) load_hiscore (HISCORE_STD) ;
  450.   else load_hiscore (HISCORE_CHALLENGE) ;
  451.  
  452.   /* Demande le nom et calcule les hiscores par continuation si le score */
  453.   /* courant est superieur ou egal au plus bas score effectue */
  454.   if (score >= hiscores[HS_SIZE-1].score)
  455.     FrmPopupForm (NAME_FORM_ID) ;
  456. }
  457.  
  458.  
  459.  
  460.  
  461.  
  462.  
  463. Boolean HiScEventHandler (EventPtr event)
  464. {
  465.   switch (event->eType) 
  466.     {
  467.     case frmOpenEvent:
  468.       FrmDrawForm (FrmGetActiveForm ()) ;
  469.       /* Il faut afficher les scores */
  470.       {
  471.     unsigned char tmp_score_text[10] ;
  472.     unsigned i ;
  473.  
  474.     for (i = 0; i < HS_SIZE; i++) {
  475.       WinDrawChars (hiscores[i].name, StrLen (hiscores[i].name),
  476.             20, 15+(i*10)) ;
  477.       StrIToA (tmp_score_text, hiscores[i].score) ;
  478.       WinDrawChars (tmp_score_text, StrLen (tmp_score_text),
  479.             50, 15+(i*10)) ;
  480.     }
  481.       }
  482.       return (TRUE) ;
  483.       break ;
  484.  
  485.     case ctlSelectEvent:
  486.       FrmReturnToForm (0) ;
  487.       return (TRUE) ;
  488.       break ;
  489.     }
  490.  
  491.   return (FALSE) ;
  492. }
  493.  
  494.  
  495.  
  496.  
  497. Boolean RuleEventHandler (EventPtr event)
  498. {
  499.   switch (event->eType) 
  500.     {
  501.     case frmOpenEvent:
  502.       FrmDrawForm (FrmGetActiveForm ()) ;
  503.       break ;
  504.  
  505.     case ctlSelectEvent:
  506.       FrmReturnToForm (0) ;
  507.       break ;
  508.     }
  509.   return (FALSE) ;
  510. }
  511.  
  512.  
  513.  
  514. Boolean NameEventHandler
  515.  (EventPtr event)
  516. {
  517.   FormPtr form ;
  518.  
  519.   switch (event->eType) 
  520.     {
  521.     case frmOpenEvent:
  522.       form = FrmGetActiveForm () ;
  523.       FrmDrawForm(form) ;
  524.       FrmSetFocus (form, FrmGetObjectIndex (form, NAME_FIELD_ID)) ;
  525.       return (TRUE) ;
  526.       break ;
  527.  
  528.     case ctlSelectEvent:
  529.       {
  530.     DmOpenRef same_db ;
  531.     UInt index ;
  532.     VoidPtr *record ;
  533.     VoidHand handle ;
  534.     CharPtr textptr ;
  535.     FieldPtr name_field ;
  536.  
  537.     form = FrmGetActiveForm () ;
  538.     name_field = FrmGetObjectPtr (form,
  539.                       FrmGetObjectIndex (form,
  540.                              NAME_FIELD_ID)) ;
  541.     textptr = FldGetTextPtr (name_field) ;
  542.     if (textptr) StrCopy (hiscores[HS_SIZE].name, textptr) ;
  543.     else StrCopy (hiscores[HS_SIZE].name, "-----") ;
  544.     hiscores[HS_SIZE].score = score ;
  545.  
  546.     /* Maintenant, on calcule la nouvelle liste de hiscores */
  547.     SysQSort (hiscores, HS_SIZE+1, sizeof (struct score_entry),
  548.           (CmpFuncPtr)SortScore, 0) ;
  549.  
  550.     /* La base existe forcement deja */
  551.     same_db = DmOpenDatabase (0, DmFindDatabase (0, SAME_HISCORES_DB_NAME),
  552.                   dmModeReadWrite) ;
  553.     /* On enregistre ces hiscores */
  554.     if (game_mode == STANDARD_GAME) index = 0 ;
  555.     else index = 1 ;
  556.     /* On recupere l'ancien record */
  557.     handle = DmGetRecord (same_db, index) ;
  558.     record = MemHandleLock (handle) ;
  559.     DmWrite (record, 0, (VoidPtr*)hiscores,
  560.          HS_SIZE*sizeof(struct score_entry)) ;
  561.     MemHandleUnlock (handle) ;
  562.     DmReleaseRecord (same_db, index, FALSE) ;
  563.     DmCloseDatabase (same_db) ;
  564.  
  565.     FrmReturnToForm (0) ;
  566.     /* Maintenant, on affiche les hiscores */
  567.     FrmPopupForm (HISCORE_FORM_ID) ;
  568.     return (TRUE) ;
  569.       }
  570.     break ;
  571.     }
  572.  
  573.   return (FALSE) ;
  574. }
  575.  
  576.  
  577. Boolean MainEventHandler (EventPtr event)
  578. {
  579.  
  580.   switch (event->eType) 
  581.     {
  582.     case frmOpenEvent:
  583.       FrmDrawForm (FrmGetActiveForm ()) ;
  584.       if (game_running) {
  585.     /* Il y a un jeu en cours, donc on l'affiche directement */
  586.     WinDrawChars ("          ", 10, 110, 0) ;
  587.     refresh_whole () ;
  588.     /* Si on avait commence un challenge, il faut afficher */
  589.     /* quelle etait la couleur de ce dernier.              */
  590.     if (game_mode != STANDARD_GAME) refresh_challenge_indicator () ;
  591.     refresh_score () ;
  592.       }
  593.       else {
  594.     /* Aucun jeu en cours. Donc page de presentation */
  595.     WinDrawInvertedChars ("The Same Game", 13, 43, 50) ;
  596.     WinDrawChars ("Written by", 10, 55, 70) ;
  597.     WinDrawChars ("Franτois Pessaux", 16, 42, 90) ;
  598.     WinDrawChars ("V 1.0", 5, 65, 110) ;
  599.     WinDrawChars ("Use menu to start", 17, 40, 145) ;
  600.       }
  601.       return (TRUE) ;
  602.       break ;
  603.  
  604.     case penDownEvent:
  605.       if (game_running) {
  606.     handle_turn ((char) (event->screenX / PATTERN_SIZE),
  607.              (char) ((event->screenY - Y_OFFSET) /
  608.                  PATTERN_SIZE)) ;
  609.     if (check_game_over ()) game_over () ;
  610.       }
  611.       return (TRUE) ;
  612.       break ;
  613.  
  614.     case menuEvent:
  615.       switch (event->data.menu.itemID)
  616.     {
  617.     case MENUITEM_NEWGAME_ID:
  618.       {
  619.         RectangleType score_zone = { 110, 0, 50, 10 } ;
  620.  
  621.         init_bits () ;
  622.         score = 0 ;
  623.         game_mode = STANDARD_GAME ;
  624.         game_running = TRUE ;
  625.         /* Efface la zone de score */
  626.         WinEraseRectangle (&score_zone, 0) ;
  627.         refresh_whole () ;
  628.         refresh_score () ;
  629.         /* Effacer le challenge indicator s'il y en avait un */
  630.         score_zone.topLeft.x = 90 ;
  631.         score_zone.topLeft.y = 2 ;
  632.         score_zone.extent.x = PATTERN_SIZE ;
  633.         score_zone.extent.y = PATTERN_SIZE ;
  634.         WinEraseRectangle (&score_zone, 0) ;
  635.         /* On verifie quand meme que le jeu n'est pas deja fini   */
  636.         /* au cas ou la configuration generee serait vraiment pas */
  637.         /* fameuse et n'aurait pas, d'origine, de solution. */
  638.         if (check_game_over ()) game_over () ;
  639.       }
  640.     return (TRUE) ;
  641.     break ;
  642.  
  643.     case MENUITEM_NEWCHALLENGE_ID:
  644.       {
  645.         RectangleType score_zone = { 110, 0, 50, 10 } ;
  646.  
  647.         init_bits () ;
  648.         score = 0 ;
  649.         /* Le jeu est en mode challenge, mais la couleur n'a pas */
  650.         /* encore ete determinee */
  651.         game_mode = CHALLENGE_GAME_UNDETERMINATED ;
  652.         game_running = TRUE ;
  653.         /* Efface la zone de score */
  654.         WinEraseRectangle (&score_zone, 0) ;
  655.         refresh_whole () ;
  656.         refresh_score () ;
  657.         refresh_challenge_indicator () ;
  658.         /* On verifie quand meme que le jeu n'est pas deja fini   */
  659.         /* au cas ou la configuration generee serait vraiment pas */
  660.         /* fameuse et n'aurait pas, d'origine, de solution. */
  661.         if (check_game_over ()) game_over () ;
  662.       }
  663.     return (TRUE) ;
  664.     break ;
  665.  
  666.     case MENUITEM_ABOUT_ID:
  667.       FrmAlert (ALERT_INFO_ID) ;
  668.       return (TRUE) ;
  669.       break ;
  670.  
  671.     case MENUITEM_STDHISCORE_ID:
  672.       load_hiscore (HISCORE_STD) ;
  673.       FrmPopupForm (HISCORE_FORM_ID) ;
  674.       return (TRUE) ;
  675.       break ;
  676.  
  677.     case MENUITEM_CHALHISCORE_ID:
  678.       load_hiscore (HISCORE_CHALLENGE) ;
  679.       FrmPopupForm (HISCORE_FORM_ID) ;
  680.       return (TRUE) ;
  681.       break ;
  682.  
  683.     case MENUITEM_RULE_ID:
  684.       FrmPopupForm (RULE_FORM_ID) ;
  685.       return (TRUE) ;
  686.       break ;
  687.     }
  688.       break ;
  689.     }
  690.  
  691.   return (FALSE) ;
  692. }
  693.  
  694.  
  695. Boolean LoadCurrentGame (void)
  696. {
  697.   DmOpenRef same_db ;
  698.   LocalID db_id ;
  699.  
  700.   /* Chargement de la partie eventuellement en cours... A faire */
  701.   db_id = DmFindDatabase (0, SAME_STATE_DB_NAME) ;
  702.   if (db_id)
  703.     {
  704.       same_db = DmOpenDatabase (0, db_id, dmModeReadWrite) ;
  705.       if (same_db)
  706.     {
  707.       VoidPtr *record ;
  708.       VoidHand handle ;
  709.  
  710.       /* Si la base existe, c'est qu'il y a un jeu en cours */
  711.       /* On charge le tableau en cours */
  712.       handle = DmGetRecord (same_db, 0) ;
  713.       record = MemHandleLock (handle) ;
  714.       MemMove ((unsigned char*)bits, (unsigned char*)record,
  715.            BITS_WIDTH*BITS_HEIGHT) ;
  716.       MemHandleUnlock (handle) ;
  717.       DmReleaseRecord (same_db, 0, FALSE) ;
  718.       /* On charge le nombre de cases restant a supprimer */
  719.       handle = DmGetRecord (same_db, 1) ;
  720.       record = MemHandleLock (handle) ;
  721.       remaining_cases = *((unsigned char*)record) ;
  722.       MemHandleUnlock (handle) ;
  723.       DmReleaseRecord (same_db, 1, FALSE) ;
  724.       /* On charge le score */
  725.       handle = DmGetRecord (same_db, 2) ;
  726.       record = MemHandleLock (handle) ;
  727.       score =  *((unsigned int*)record) ;
  728.       MemHandleUnlock (handle) ;
  729.       DmReleaseRecord (same_db, 2, FALSE) ;
  730.       /* On charge le mode de jeu */
  731.       handle = DmGetRecord (same_db, 3) ;
  732.       record = MemHandleLock (handle) ;
  733.       game_mode =  *((unsigned char*)record) ;
  734.       MemHandleUnlock (handle) ;
  735.       DmReleaseRecord (same_db, 3, FALSE) ;
  736.       /* Le jeu est donc en cours */
  737.       game_running = TRUE ;
  738.       DmCloseDatabase (same_db) ;
  739.       db_id = DmFindDatabase (0, SAME_STATE_DB_NAME) ;
  740.       DmDeleteDatabase (0, db_id) ;
  741.       /* On notifie qu'un jeu etait en cours */
  742.       return (TRUE) ;
  743.     }
  744.     }
  745.  
  746.   /* On notifie quaucun jeu n'etait en cours */
  747.   return (FALSE) ;
  748. }
  749.  
  750.  
  751. void SaveCurrentGame ()
  752. {
  753.   DmOpenRef same_db ;
  754.   VoidPtr record ;
  755.   VoidHand handle ;
  756.   UInt index ;
  757.  
  758.   /* Cree la database en ecrasant l'eventuelle ancienne */
  759.   DmCreateDatabase (0, SAME_STATE_DB_NAME, SAME_APP_ID, SAME_DB_TYPE, FALSE) ;
  760.   /* Ouvre la database en lecture */
  761.   same_db = DmOpenDatabase (0, DmFindDatabase (0, SAME_STATE_DB_NAME),
  762.                 dmModeReadWrite) ;
  763.   /* Alloue le record pour le tableau */
  764.   index = 0 ;
  765.   handle = DmNewRecord (same_db, &index, BITS_WIDTH*BITS_HEIGHT) ;
  766.   record = MemHandleLock (handle) ;
  767.   DmWrite (record, 0, (VoidPtr*)bits, BITS_WIDTH*BITS_HEIGHT) ;
  768.   MemHandleUnlock (handle) ;
  769.   DmReleaseRecord (same_db, index, FALSE) ;
  770.  
  771.   /* Alloue le record pour le nombre de case restantes */
  772.   index = 1 ;
  773.   handle = DmNewRecord (same_db, &index, sizeof(unsigned char)) ;
  774.   record = MemHandleLock (handle) ;
  775.   DmWrite (record, 0, (VoidPtr*)&remaining_cases, sizeof(unsigned char)) ;
  776.   MemHandleUnlock (handle) ;
  777.   DmReleaseRecord (same_db, index, FALSE) ;
  778.  
  779.   /* Alloue le record pour le score */
  780.   index = 2 ;
  781.   handle = DmNewRecord (same_db, &index, sizeof(unsigned int)) ;
  782.   record = MemHandleLock (handle) ;
  783.   DmWrite (record, 0, (VoidPtr*)&score, sizeof(unsigned int)) ;
  784.   MemHandleUnlock (handle) ;
  785.   DmReleaseRecord (same_db, index, FALSE) ;
  786.  
  787.   /* Alloue le record pour le mode de jeu */
  788.   index = 3 ;
  789.   handle = DmNewRecord (same_db, &index, sizeof(unsigned char)) ;
  790.   record = MemHandleLock (handle) ;
  791.   DmWrite (record, 0, (VoidPtr*)&game_mode, sizeof(unsigned char)) ;
  792.   MemHandleUnlock (handle) ;
  793.   DmReleaseRecord (same_db, index, FALSE) ;
  794.   DmCloseDatabase (same_db) ;
  795. }
  796.  
  797.  
  798. void EventLoop (void)
  799. {
  800.   EventType event ;
  801.   FormPtr form ;
  802.   int formID ;
  803.   short error ;
  804.  
  805.   do {
  806.     EvtGetEvent (&event, -1) ;
  807.     if (SysHandleEvent (&event)) continue ;
  808.     if (MenuHandleEvent ((void *)0, &event, &error)) continue ;
  809.  
  810.     if (event.eType == frmLoadEvent)
  811.       {
  812.     formID = event.data.frmLoad.formID ;
  813.     form = FrmInitForm (formID) ;
  814.     FrmSetActiveForm (form) ;
  815.     switch (formID) 
  816.       {
  817.       case GAME_FORM_ID:
  818.         if (LoadCurrentGame ())
  819.           {
  820.         /* Il y avait un jeu en cours, donc on le lance directement */
  821.         game_running = TRUE ;
  822.           }
  823.         FrmSetEventHandler (form, (FormEventHandlerPtr) MainEventHandler) ;
  824.         break;
  825.  
  826.       case HISCORE_FORM_ID:
  827.         FrmSetEventHandler (form, (FormEventHandlerPtr) HiScEventHandler) ;
  828.         break ;
  829.  
  830.       case NAME_FORM_ID:
  831.         FrmSetEventHandler (form, (FormEventHandlerPtr) NameEventHandler) ;
  832.         break ;
  833.  
  834.       case RULE_FORM_ID:
  835.         FrmSetEventHandler (form, (FormEventHandlerPtr) RuleEventHandler) ;
  836.         break ;
  837.       }
  838.       }
  839.     FrmDispatchEvent (&event) ;
  840.   } while (event.eType != appStopEvent) ;
  841.  
  842.   /* Backup de l'etat de la partie s'il y a une partie en cours... */
  843.   if (game_running) SaveCurrentGame () ;
  844. }
  845.  
  846.  
  847.  
  848. DWord  PilotMain (Word cmd, Ptr cmdPBP, Word launchFlags)
  849. {
  850.   if (cmd == sysAppLaunchCmdNormalLaunch)
  851.     {
  852.       FrmGotoForm (GAME_FORM_ID) ;
  853.       EventLoop () ;
  854.     }
  855.   return (0) ;
  856. }
  857.